//
// Created by Administrator on 2024/11/6.
//


#include "FileInstanceProcessor.h"
#include <variant>


FileInstanceProcessor::FileInstanceProcessor() {
    pool = ConnectionPool::getInstance();

}

std::vector<FileInstance> FileInstanceProcessor::getList(std::string &hosp_code, std::string &study_instance_uid) {
    auto client = pool->getConnection();
    if (!client) {
        delete client->connect;
        throw std::runtime_error("pool get connection field: ");
    }

    auto sql = "SELECT id FROM file_instance WHERE hosp_code = ? and study_instance_uid =?";
    sql::PreparedStatement *prep_stmt = client->connect->prepareStatement(sql);
    prep_stmt->setString(1, hosp_code);
    prep_stmt->setString(2, study_instance_uid);

    sql::ResultSet *res = prep_stmt->executeQuery();
    std::vector<FileInstance> results;
    while (res->next()) {
        FileInstance inst;
        inst.id = res->getInt64("id");
        results.push_back(inst);
    }

    delete client->connect;
    delete res;
    delete prep_stmt;
    return results;
}

bool FileInstanceProcessor::create(const DicomStudy &study) {

    auto client = pool->getConnection();
    if (!client) {
        delete client->connect;
        throw std::runtime_error("pool get connection field: ");
    }
    using MemberPtr = std::variant<std::string, int>;
    std::map<std::string, MemberPtr> dataMap = {
            {"hosp_code",               study.pipeline},
            {"install_code",            study.installCode},
            {"study_instance_uid",      study.uid},
            {"study_id",                study.id},
            {"study_date",              study.date},
            {"study_time",              study.time},
            {"study_description",       study.description},
            {"exam_ref_id",             study.examRefId},
            {"body_part",               study.bodyPart},
            {"clinical_impression",     study.clinicalImpression},
            {"station_name",            study.stationName},
            {"manufacturer_model_name", study.manufacturerModelName},
            {"src_aet",                 study.srcAet},
            {"transfer_syntax_uid",     study.transferSyntaxUid},
            {"modality",                study.modality},
            {"storage_size",            study.storageSize},
            {"accession_number",        study.accessionNumber},
            {"patient_id",              study.patient.id},
            {"patient_age",             study.patient.age},
            {"patient_dob",             study.patient.dateOfBirth},
            {"patient_gender",          study.patient.gender},
            {"patient_name",            study.patient.name},
            {"series_count",            study.seriesCount},
            {"count_instances",         study.instanceCount},
            {"storage_path_prefix",     study.rootPath},
            {"search_id",               study.studySearchId},
            {"file_system_id",          2},
            {"created_time",            DateUtils::getCurrentTimeString()}
    };


    std::vector<std::string> columns;
    std::vector<MemberPtr> fieldValues;
    for (const auto &it: dataMap) {
        columns.push_back(it.first);
        fieldValues.push_back(it.second);
    }

    std::string columnsStr = std::format("{}", StringUtils::join(columns, ","));
    std::string valuesStr = "?";
    std::for_each(columns.begin() + 1, columns.end(), [&valuesStr](const auto &) { valuesStr += ", ?"; });
    std::string query = std::format("INSERT INTO file_instance({}) VALUES({})", columnsStr, valuesStr);

    sql::PreparedStatement *prep_stmt = client->connect->prepareStatement(query);
    size_t index = 1;
    for (auto &val: fieldValues) {
        if (std::holds_alternative<std::string>(val)) {
            prep_stmt->setString(index++, std::get<std::string>(val));
        } else if (std::holds_alternative<int>(val)) {
            prep_stmt->setInt(index++, std::get<int>(val));
        }
    }

    int rows_affected = prep_stmt->executeUpdate();
    delete prep_stmt;
    delete client->connect;

    if (rows_affected < 1) {
        throw std::runtime_error("Insert operation did not affect exactly one row! ");
    }

    return true;
}

bool FileInstanceProcessor::update(std::string &request_id, std::string &hosp_code, std::string &study_instance_uid) {

    auto client = pool->getConnection();
    if (!client) {
        delete client->connect;
        throw std::runtime_error("pool get connection field: ");
    }

    const char *query = "INSERT INTO dicom_archive(request_id, hosp_code, study_instance_uid, task_status) VALUES(?, ?, ?, ?)";
    sql::PreparedStatement *prep_stmt = client->connect->prepareStatement(query);
    prep_stmt->setString(1, request_id);
    prep_stmt->setString(2, hosp_code);
    prep_stmt->setString(3, study_instance_uid);
    prep_stmt->setInt(4, 2);

    int rows_affected = prep_stmt->executeUpdate();
    delete prep_stmt;
    delete client->connect;

    if (rows_affected < 1) {
        throw std::runtime_error("Insert operation did not affect exactly one row! ");
    }

    return true;
}
